package com.itheima.grow.util;

import cn.hutool.core.io.resource.ResourceUtil;
import com.itextpdf.text.pdf.BaseFont;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * description
 *
 * @Author:weiwei.xie
 * @Date 2022/5/5
 */
@Slf4j
@Component
public class PdfHelper {

    /**
     * classpath路径
     */
    private String classpath = getClass().getResource("/").getPath();

    /**
     * 指定FreeMarker模板文件的位置
     */
    private String templatePath = "/templates";

    /**
     * freeMarker模板文件名称
     */
    private String templateFileName = "pdf.ftl";

    /**
     * 图片路径 —— 默认是classpath下面的images文件夹
     */
    private String imagePath = "/images/";

    /**
     * 字体资源文件 存放路径
     */
    private String fontPath = "fonts/";

    /**
     * 字体-仿宋字体[simfang.ttc]   [黑体][simhei.ttf]
     */
    private String font = "static/simfang.ttf";

    /**
     * 指定编码
     */
    private String encoding = "UTF-8";

    @Value("{$file.path}")
    private String filePath;

    public byte[] createPDF(Map<String, Object> data, String templateName) throws Exception {
        // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置
        ITextRenderer renderer = new ITextRenderer();
        // 解决itext生成中文不显示问题
//        String FONT_TTF_PATH = ResourceUtil.getResource("").getPath().replace("target/classes/", "").concat("src/main/resources/") + font;
        // 设置 css中 的字体样式（暂时仅支持宋体和黑体）
//        renderer.getFontResolver().addFont(FONT_TTF_PATH, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

        renderer.getFontResolver().addFont(new ClassPathResource("templates/Overpass-Black.ttf").getPath(), BaseFont.IDENTITY_H, true);
        renderer.getFontResolver().addFont(new ClassPathResource("templates/Overpass-ExtraBold.ttf").getPath(), BaseFont.IDENTITY_H, true);
        renderer.getFontResolver().addFont(new ClassPathResource("templates/Overpass-Medium.ttf").getPath(), BaseFont.IDENTITY_H, true);
        renderer.getFontResolver().addFont(new ClassPathResource("templates/Overpass-Regular.ttf").getPath(), BaseFont.IDENTITY_H, true);

        StringTemplateLoader stringLoader = new StringTemplateLoader();
        String templateString = getFileData("templates/" + templateName);
        stringLoader.putTemplate("myTemplate", templateString);
        cfg.setTemplateLoader(stringLoader);
        Template tpl = cfg.getTemplate("myTemplate", "utf-8");
        StringWriter writer = new StringWriter();

        // 把null转换成""
        Map<String, Object> dataMap = CommunalTool.objectToMap(data);
        // 将数据输出到html中
        tpl.process(dataMap, writer);
        writer.flush();

        String html = writer.toString();
        // 把html代码传入渲染器中
        renderer.setDocumentFromString(html);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        renderer.layout();
        renderer.createPDF(out);
        renderer.finishPDF();
        byte[] fileByte = out.toByteArray();
        out.flush();
        out.close();
        return fileByte;
    }

    /**
     * 生成pdf
     * @param data 传入到freemarker模板里的数据
     * @param out  生成的pdf文件流
     */
    public OutputStream createPDF(Map<String, Object> data, OutputStream out, String templateName) throws Exception {
        // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置
        ITextRenderer renderer = new ITextRenderer();
        // 设置 css中 的字体样式（暂时仅支持宋体和黑体）
        // 解决itext生成中文不显示问题
        String FONT_TTF_PATH = ResourceUtil.getResource("").getPath().replace("target/classes/", "").concat("src/main/resources/") + font;
        // 设置 css中 的字体样式（暂时仅支持宋体和黑体）
        renderer.getFontResolver().addFont(FONT_TTF_PATH, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

        StringTemplateLoader stringLoader = new StringTemplateLoader();
        String templateString = getFileData("templates/" + templateName);
        stringLoader.putTemplate("myTemplate", templateString);
        cfg.setTemplateLoader(stringLoader);
        Template tpl = cfg.getTemplate("myTemplate", "utf-8");
        StringWriter writer = new StringWriter();

        // 把null转换成""
        Map<String, Object> dataMap = CommunalTool.objectToMap(data);
        // 将数据输出到html中
        tpl.process(dataMap, writer);
        writer.flush();

        String html = writer.toString();
        // 把html代码传入渲染器中
        renderer.setDocumentFromString(html);

        renderer.layout();
        renderer.createPDF(out, false);
        renderer.finishPDF();
        return out;
    }

    // 读取jar中的ftl模版文件
    private String getFileData(String path) {
        InputStream stream = getClass().getClassLoader().getResourceAsStream(path);
        // log.info("infile:"+infile);
        StringBuffer sb = new StringBuffer();
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
            String s = null;
            while ((s = br.readLine()) != null) {
                sb.append(s);
            }
            br.close();
            return sb.toString();
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            return null;
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (Exception e) {
                    log.error(e.getMessage(),e);
                }
            }
        }
    }

    /**
     * pdf下载
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称(带后缀.ftl)
     * @param listVars     模板参数集
     * @param response     HttpServletResponse
     * @param fileName     下载文件名称(带文件扩展名后缀)
     */
    public static void download(FreeMarkerConfigurer configurer, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response, String fileName, HttpServletRequest request) {
        // 设置编码、文件ContentType类型、文件头、下载文件名
        String userAgent = request.getHeader("User-Agent");


        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
//        response.setContentType("application/pdf");
        try {
            String formFileName;
            // 针对IE或者以IE为内核的浏览器：
            if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
                formFileName = java.net.URLEncoder.encode(fileName, "UTF-8");
            } else {
                // 非IE浏览器的处理：
                formFileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            }
            response.setHeader("Content-Disposition", "attachment;fileName=" + formFileName);
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        }
        try (ServletOutputStream out = response.getOutputStream()) {
            generateAll(configurer, templateName, out, listVars);
            out.flush();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 核心: 根据freemarker模板生成pdf文档
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称
     * @param out          输出流
     * @param listVars     freemarker模板参数
     * @throws Exception 模板无法找到、模板语法错误、IO异常
     */
    private static void generateAll(FreeMarkerConfigurer configurer, String templateName, OutputStream out, List<Map<String, Object>> listVars) throws Exception {
        if (CollectionUtils.isEmpty(listVars)) {
            log.warn("警告:freemarker模板参数为空!");
            return;
        }

        ITextRenderer renderer = new ITextRenderer();
        Document doc = generateDoc(configurer, templateName, listVars.get(0));
        renderer.setDocument(doc, null);
        //设置字符集(宋体),此处必须与模板中的<body style="font-family: SimSun">一致,区分大小写,不能写成汉字"宋体"
//        ITextFontResolver fontResolver = renderer.getFontResolver();
//        fontResolver.addFont("simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        //展现和输出pdf
        renderer.layout();
        renderer.createPDF(out, false);

        //根据参数集个数循环调用模板,追加到同一个pdf文档中
        //(注意:此处从1开始,因为第0是创建pdf,从1往后则向pdf中追加内容)
        for (int i = 1; i < listVars.size(); i++) {
            Document docAppend = generateDoc(configurer, templateName, listVars.get(i));
            renderer.setDocument(docAppend, null);
            renderer.layout();
            renderer.writeNextDocument(); //写下一个pdf页面
        }
        renderer.finishPDF(); //完成pdf写入
    }

    /**
     * 按模板和参数生成html字符串,再转换为flying-saucer识别的Document
     *
     * @param templateName freemarker模板名称
     * @param variables    freemarker模板参数
     * @return Document
     */
    private static Document generateDoc(FreeMarkerConfigurer configurer, String templateName, Map<String, Object> variables)  {
        Template tp;
        try {
            tp = configurer.getConfiguration().getTemplate(templateName);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return null;
        }

        StringWriter stringWriter = new StringWriter();
        try(BufferedWriter writer = new BufferedWriter(stringWriter)) {
            try {
                tp.process(variables, writer);
                writer.flush();
            } catch (TemplateException e) {
                log.error("模板不存在或者路径错误", e);
            } catch (IOException e) {
                log.error("IO异常", e);
            }
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return builder.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
        }catch (Exception e){
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 获取字体路径
     *
     * @return
     * @throws IOException
     */
    private String getFondPath() throws IOException {
        String fontPath = System.getProperty("user.dir");
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("fonts/simsun.ttc");
        //在根目录生成一个文件
        File targetFile = new File(fontPath + "/simsun.ttc");
        // //将流转成File格式
        FileUtils.copyInputStreamToFile(inputStream, targetFile);

        InputStream arialuniStream = getClass().getClassLoader().getResourceAsStream("fonts/arialuni.ttf");
        File arialuni = new File(fontPath + "/arialuni.ttf");
        FileUtils.copyInputStreamToFile(arialuniStream, arialuni);
        return fontPath;
    }

    public void setClasspath(String classpath) {
        this.classpath = classpath;
    }


    public void setTemplatePath(String templatePath) {
        this.templatePath = templatePath;
    }


    public void setTemplateFileName(String templateFileName) {
        this.templateFileName = templateFileName;
    }


    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }


    public void setFontPath(String fontPath) {
        this.fontPath = fontPath;
    }


    public void setFont(String font) {
        this.font = font;
    }


    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

}
